//---------------------------------------------------------------------------

#include "vcl_stl.h"
#pragma hdrstop

#include "h_base.h"
#include "h_xml.h"
#include "h_global.h"
using namespace h_base;
using namespace h_xml;

#include "t_mainform.h"
#include "h_stage.h"
#include "h_g.h"
#include "h_stageicon.h"
#include "h_designcontrol.h"
#include "h_i18n.h"

#include <Clipbrd.hpp>

//---------------------------------------------------------------------------

__fastcall HDesignControl::HDesignControl(
   Classes::TComponent* AOwner) : TScrollingWinControl(AOwner)
{
   Canvas = new TControlCanvas;
   Canvas->Control = this;

   DragAction = daNone;

   OnKeyDown = FormKeyDown;
   OnKeyUp = FormKeyUp;
   PopupMenu = MainForm->PopupMenu2;
   DoubleBuffered = true;

   HorzScrollBar->Tracking = true;
   VertScrollBar->Tracking = true;
   TabStop = true;
   AutoScroll = true;
}

__fastcall HDesignControl::~HDesignControl(void)
{
   for( map<UTF8String, HStage*>::iterator Iter = ControlMap.begin();
        Iter != ControlMap.end(); ++Iter )
      delete Iter->second;

   delete Canvas;
}

bool __fastcall HDesignControl::GroupExist(const UTF8String &Name)
{
   for( map<UTF8String, HStage*>::iterator Iter = ControlMap.begin();
        Iter != ControlMap.end(); ++Iter )
   {
      HStage *Stage = Iter->second;
      if ( Stage->GetType() != "GROUP" )
      {
         continue;
      }
      HStageGroup::HInfo Info;
      HStageGroup *Group = (HStageGroup*)Stage;
      Group->GetInfo(Info);
      if ( Info.Group == Name )
      {
         return true;
      }
   }

   return false;
}

void __fastcall HDesignControl::WndProc(TMessage &Message)
{
   if( Message.Msg >= WM_MOUSEFIRST &&
       Message.Msg <= WM_MOUSELAST &&
       Message.Msg != WM_MOUSEWHEEL )
   {
      TControl::WndProc(Message);
      return;
   }

   if ( Message.Msg == WM_MOUSEWHEEL )
   {
      int i = (signed short)(Message.WParam >> 16);
      int OldPos = VertScrollBar->Position;
      VertScrollBar->Position -= i;
      if ( VertScrollBar->Position != OldPos )
      {
         Invalidate();
      }
   }

   if ( Message.Msg == WM_HSCROLL ||
        Message.Msg == WM_VSCROLL )
   {
      Invalidate();
   }

   inherited::WndProc(Message);
}

void __fastcall HDesignControl::WMGetDlgCode(TWMGetDlgCode &Message)
{
   Message.Result |= DLGC_WANTARROWS;
}

void __fastcall HDesignControl::WMSetCursor(TWMSetCursor &Message)
{
   if( MainForm->CurrentAction == TMainForm::caUnlink )
      ::SetCursor(LoadCursor(NULL, IDC_CROSS));
   else
      ::SetCursor(LoadCursor(NULL, IDC_ARROW));
}

void __fastcall HDesignControl::WMPaint(TWMPaint &Message)
{
   HDC DC;
   TPaintStruct PS;

   if( Message.DC == 0)
      DC = BeginPaint(Handle, &PS);
   else
      DC = Message.DC;

   Canvas->Lock();
   Canvas->Handle = DC;

   Canvas->Brush->Color = h_g::Background->Canvas->Pixels[0][0];
   Canvas->FillRect(Canvas->ClipRect);

   static bool First = true;
   static int MaxDesignerWidth;
   static int MaxDesignerHeight;

   if ( First )
   {
      MaxDesignerWidth = Canvas->ClipRect.Width();
      MaxDesignerHeight = Canvas->ClipRect.Height();
      First = false;
   }

   Canvas->Draw(MaxDesignerWidth - h_g::Background->Width,
      MaxDesignerHeight - h_g::Background->Height, h_g::Background);

   if ( h_g::CustLogoFlag && h_g::CustLogo != NULL )
   {
      Canvas->Draw(MaxDesignerWidth - h_g::CustLogo->Width,
         MaxDesignerHeight - h_g::CustLogo->Height, h_g::CustLogo);
   }

   TMainForm::HInfo Info;

   MainForm->GetInfo(Info);

   if ( Info.Banner != "" )
   {
      TFont *FontBak = new TFont();
      FontBak->Assign(Canvas->Font);
      Canvas->Font->Name = Info.BannerFontName;
      Canvas->Font->Size = Info.BannerFontSize;
      Set<TFontStyle, fsBold, fsStrikeOut> FontStyle;
      IntToFontStyle(Info.BannerFontStyle, FontStyle);
      Canvas->Font->Style = FontStyle;
      Canvas->Font->Charset = 1;
      Canvas->Font->Color = h_g::TextColor;
      Canvas->Brush->Style = bsClear; 
      int TextWidth = Canvas->TextWidth(Info.Banner);
      int TextHeight = Canvas->TextHeight(Info.Banner);
      const int Margin = 10;
      switch ( Info.BannerPosition )
      {
      case 0 : // Top left corner
         Canvas->TextOut(Margin - HorzScrollBar->Position,
            Margin - VertScrollBar->Position, Info.Banner);
         break;
      case 1 : // Top right corner
         Canvas->TextOut(MaxDesignerWidth - TextWidth - Margin -
            HorzScrollBar->Position,
            Margin - VertScrollBar->Position, Info.Banner);
         break;
      case 2 : // Bottom left corner
         Canvas->TextOut(Margin - HorzScrollBar->Position,
            MaxDesignerHeight - TextHeight - Margin -
            VertScrollBar->Position, Info.Banner);
         break;
      case 3 : // Bottom right corner
         Canvas->TextOut(MaxDesignerWidth - TextWidth - Margin -
            HorzScrollBar->Position,
            MaxDesignerHeight - TextHeight - Margin -
            VertScrollBar->Position, Info.Banner);
         break;
      case 4 : // Top middle
         Canvas->TextOut(( MaxDesignerWidth - TextWidth ) / 2 -
            HorzScrollBar->Position,
            Margin - VertScrollBar->Position, Info.Banner);
         break;
      case 5 : // Bottom middle
         Canvas->TextOut(( MaxDesignerWidth - TextWidth ) / 2 -
            HorzScrollBar->Position,
            MaxDesignerHeight - TextHeight - Margin -
            VertScrollBar->Position, Info.Banner);
         break;
      }
      Canvas->Font->Assign(FontBak);
      delete FontBak;
   }

   for ( map<UTF8String, HLinkInfo>::iterator Iter = LinkMap.begin();
       Iter != LinkMap.end(); ++Iter )
   {
      DrawLink(Iter->first, true);
   }

   for ( map<UTF8String, HLinkInfo>::iterator Iter = LinkMap.begin();
       Iter != LinkMap.end(); ++Iter )
   {
      DrawLink(Iter->first, false);
   }

   Canvas->Handle = 0;
   Canvas->Unlock();

   PaintControls(DC, NULL);

   if ( Message.DC == 0 )
   {
      EndPaint(Handle, &PS);
   }
}

void __fastcall HDesignControl::WMLButtonDown(TWMLButtonDown &Message)
{
   CancelDragAction();
   DragX = Message.XPos;
   DragY = Message.YPos;
   DragDeltaX = 0;
   DragDeltaY = 0;
   SetFocus();

   if ( MainForm->CurrentAction == TMainForm::caSelect )
   {
      UTF8String Name = FindControlByPos(Message.XPos, Message.YPos);
      if ( Name == "" )
      {
         UnselectControl();
         DragAction = daBox;
         TRect Rect;
         Rect.Left = ClientOrigin.x;
         Rect.Top = ClientOrigin.y;
         Rect.Right = Rect.Left + ClientWidth;
         Rect.Bottom = Rect.Top + ClientHeight;
         ClipCursor(&Rect);
      }
      else
      {
         if ( GetKeyState(VK_CONTROL) & 0x8000 )
         {
            if ( SelectedGroup.find(Name) == SelectedGroup.end() )
            {
               SelectControl(Name);
            }
            else
            {
               UnselectControl(Name);
            }
         }
         else
         {
            if ( SelectedGroup.find(Name) == SelectedGroup.end() )
            {
               UnselectControl();
               SelectControl(Name);
            }
            DragAction = daDrag;
            Update();
            DrawDragTrack();
            TRect Rect;
            int MinLeft = 0;
            int MinTop = 0;
            bool First = true;
            for ( set<UTF8String>::iterator Iter = SelectedGroup.begin();
                  Iter != SelectedGroup.end(); Iter++ )
            {
               HStageIcon *Icon = ControlMap[*Iter]->GetIcon();
               if ( First )
               {
                  First = false;
                  MinLeft = Icon->Left;
                  MinTop = Icon->Top;
               }
               else
               {
                  if ( Icon->Left < MinLeft )
                  {
                     MinLeft = Icon->Left;
                  }
                  if ( Icon->Top < MinTop )
                  {
                     MinTop = Icon->Top;
                  }
               }
            }
            Rect.Left = ClientOrigin.x + ( Message.XPos - HorzScrollBar->Position - MinLeft );
            Rect.Top = ClientOrigin.y + ( Message.YPos - VertScrollBar->Position - MinTop );
            Rect.Right = Rect.Left + ClientWidth - ( Message.XPos - HorzScrollBar->Position - MinLeft );
            Rect.Bottom = Rect.Top + ClientHeight - ( Message.YPos - VertScrollBar->Position - MinTop );
            ClipCursor(&Rect);
         }
      }
      return;
   }

   if ( MainForm->CurrentAction == TMainForm::caLink )
   {
      UnselectControl();
      UTF8String Name = FindControlByPos(Message.XPos, Message.YPos);
      if ( Name == "" )
      {
         return;
      }
      DragAction = daLink;
      Update();
      DrawDragTrack();
      TRect Rect;
      Rect.Left = ClientOrigin.x;
      Rect.Top = ClientOrigin.y;
      Rect.Right = Rect.Left + ClientWidth;
      Rect.Bottom = Rect.Top + ClientHeight;
      ClipCursor(&Rect);
      return;
   }

   if ( MainForm->CurrentAction == TMainForm::caUnlink )
   {
      UTF8String Name = FindControlByPos(Message.XPos, Message.YPos);
      if ( Name != "" )
      {
         set<UTF8String> JobSet;
         JobSet.insert(Name);
         HCmdDeleteJob *Cmd = new HCmdDeleteJob(JobSet);
         MainForm->PushUndoStack(Cmd);
         MainForm->ButtonSelect->Down = true;
         MainForm->CurrentAction = TMainForm::caSelect;
         return;
      }
      Name = FindLinkByPos(Message.XPos, Message.YPos);
      if ( Name != "" )
      {
         HCmdRemoveLink *Cmd = new HCmdRemoveLink(LinkMap[Name].StartControl,
            LinkMap[Name].EndControl);
         MainForm->PushUndoStack(Cmd);
         MainForm->ButtonSelect->Down = true;
         MainForm->CurrentAction = TMainForm::caSelect;
      }
      return;
   }

   if ( MainForm->CurrentAction == TMainForm::caAddStage )
   {
      int X = ( Message.XPos + HorzScrollBar->Position ) % GridInterval;

      if ( X <= GridInterval / 2 )
      {
         X = Message.XPos - X;
      }
      else
      {
         X = Message.XPos - X + GridInterval;
      }

      int Y = ( Message.YPos + VertScrollBar->Position ) % GridInterval;

      if ( Y <= GridInterval / 2 )
      {
         Y = Message.YPos - Y;
      }
      else
      {
         Y = Message.YPos - Y + GridInterval;
      }
      HCmdNewJob *Cmd = new HCmdNewJob(X, Y, MainForm->StageType);
      MainForm->PushUndoStack(Cmd);
      MainForm->ButtonSelect->Down = true;
      MainForm->CurrentAction = TMainForm::caSelect;
      return;
   }
}

void __fastcall HDesignControl::WMLButtonUp(TWMLButtonUp &Message)
{
   if ( DragAction == daBox || DragAction == daDrag || DragAction == daLink )
   {
      EndDragAction();
      return;
   }
}

void __fastcall HDesignControl::WMMouseMove(TWMMouseMove &Message)
{
   if ( DragAction == daNone || DragAction == daMove )
   {
      return;
   }

   if( DragAction == daBox || DragAction == daLink )
   {
      DrawDragTrack();
      DragDeltaX = Message.XPos - DragX;
      DragDeltaY = Message.YPos - DragY;
      DrawDragTrack();
      return;
   }

   int XDelta = Message.XPos - DragX;
   int YDelta = Message.YPos - DragY;
   int XSign, YSign;

   if ( XDelta < 0 )
   {
      XDelta = -XDelta;
      XSign = -1;
   }
   else
   {
      XSign = 1;
   }

   if ( YDelta < 0 )
   {
      YDelta = -YDelta;
      YSign = -1;
   }
   else
   {
      YSign = 1;
   }
   
   XDelta = XDelta / GridInterval * GridInterval;
   YDelta = YDelta / GridInterval * GridInterval;

   if ( XDelta == DragDeltaX && YDelta == DragDeltaY )
   {
      return;
   }

   DrawDragTrack();
   DragDeltaX = XDelta * XSign;
   DragDeltaY = YDelta * YSign;
   DrawDragTrack();
}

void __fastcall HDesignControl::WMLButtonDblClk(TWMLButtonDblClk &Message)
{
   if ( MainForm->CurrentAction != TMainForm::caSelect )
   {
      return;
   }

   if ( SelectedGroup.empty() )
   {
      MainForm->DoEditBatchProperty();
   }
   else
   {
      DoEditProperty();
   }
}

void __fastcall HDesignControl::WMRButtonDown(TWMRButtonDown &Message)
{
   CancelDragAction();
   UTF8String Name = FindControlByPos(Message.XPos, Message.YPos);

   if ( Name == "" )
   {
      UnselectControl();
   }
   else
   {
      if ( SelectedGroup.find(Name) == SelectedGroup.end() )
      {
         UnselectControl();
         SelectControl(Name);
      }
   }
}

void __fastcall HDesignControl::FormKeyDown(TObject *Sender, WORD &Key,
   TShiftState Shift)
{
   if ( Key == VK_ESCAPE )
   {
      CancelDragAction();
      return;
   }

   if ( ( Key == VK_LEFT || Key == VK_RIGHT ||
          Key == VK_UP   || Key == VK_DOWN ) &&
        Shift.Contains(ssCtrl) )
   {
      if ( DragAction != daNone && DragAction != daMove )
      {
         return;
      }
      if ( SelectedGroup.empty() )
      {
         return;
      }
      if ( DragAction == daMove )
      {
         DrawDragTrack();
      }
      else
      {
         DragAction = daMove;
         DragDeltaX = 0;
         DragDeltaY = 0;
      }
      switch ( Key )
      {
      case VK_LEFT :
         DragDeltaX--;
         break;
      case VK_RIGHT :
         DragDeltaX++;
         break;
      case VK_UP :
         DragDeltaY--;
         break;
      case VK_DOWN :
         DragDeltaY++;
         break;
      }
      DrawDragTrack();
      return;
   }
}

void __fastcall HDesignControl::FormKeyUp(TObject *Sender, WORD &Key,
   TShiftState Shift)
{
   if ( Key == VK_CONTROL )
   {
      if ( DragAction == daMove )
      {
         EndDragAction();
      }
   }
}

//---------------------------------------------------------------------------

TColor __fastcall HDesignControl::CalcColor(TColor BaseColor, int Percent)
{
   unsigned char Byte1;
   unsigned char Byte2;
   unsigned char Byte3;
   unsigned char Byte4;

   Byte1 = ( (unsigned int)BaseColor & 0xff000000 ) >> 24;
   Byte2 = ( (unsigned int)BaseColor & 0x00ff0000 ) >> 16;
   Byte3 = ( (unsigned int)BaseColor & 0x0000ff00 ) >> 8;
   Byte4 = ( (unsigned int)BaseColor & 0x000000ff );

   Byte2 = Byte2 * Percent / 100;
   Byte3 = Byte3 * Percent / 100;
   Byte4 = Byte4 * Percent / 100;

   return TColor( ( Byte1 << 24 ) | ( Byte2 << 16 ) | ( Byte3 << 8 ) | Byte4 );
}

void __fastcall HDesignControl::DrawLink(const UTF8String &Name,
   bool First)
{
   int Type;
   vector<TPoint> Points;
   vector<TPoint> Points2;
   int ArrowWidth = h_g::BitmapArrow[0]->Width;
   int ArrowLength = h_g::BitmapArrow[0]->Height;

   if ( h_g::DirectLink && !First )
   {
      return;
   }

   GetLinkPoints(Name, Points, Type);

   if ( h_g::DirectLink )
   {
      TPoint Begin = Points.front();
      TPoint End = Points.back();
      Canvas->Pen->Color = h_g::LineColor;
      Canvas->MoveTo(Begin.x, Begin.y);
      Canvas->LineTo(End.x, End.y);
      Canvas->Pen->Color = clRed;
      Canvas->Brush->Color = clRed;
      Canvas->Ellipse(End.x - 2, End.y - 2, End.x + 2, End.y + 2);
      TRect Rect((Begin.x + End.x) / 2 - 3, (Begin.y + End.y) / 2 - 3,
         (Begin.x + End.x) / 2 + 3, (Begin.y + End.y) / 2 + 3);
      Canvas->Brush->Color = clGreen;
      Canvas->FillRect(Rect);
      return;
   }

   for ( int i = 0; i < Points.size(); i++ )
   {
      Points2.push_back(TPoint(Points[i].x + 1, Points[i].y + 1));
   }

   if ( First )
   {
      Canvas->Pen->Color = h_g::LineShadowColor;
      Canvas->Polyline(&Points2[0], Points2.size() - 1);
   }
   else
   {
      Canvas->Pen->Color = h_g::LineColor;
      Canvas->Polyline(&Points[0], Points.size() - 1);
   }

   TPoint End = Points.back();

   if ( First && Type != 4 )
   {
      switch( Type )
      {
      case 0 :
         Canvas->Draw(End.x - 1, End.y - ArrowWidth / 2,
            h_g::BitmapArrow[h_g::adRight]);
         break;
      case 1 :
         Canvas->Draw(End.x - ArrowLength + 1 + 1, End.y - ArrowWidth / 2,
            h_g::BitmapArrow[h_g::adLeft]);
         break;
      case 2 :
         Canvas->Draw(End.x - ArrowWidth / 2, End.y - 1,
            h_g::BitmapArrow[h_g::adDown]);
         break;
      case 3 :
         Canvas->Draw(End.x - ArrowWidth / 2, End.y - ArrowLength + 1 + 1,
            h_g::BitmapArrow[h_g::adUp]);
         break;
      }
   }

   return;
}

void __fastcall HDesignControl::GetLinkPoints(const UTF8String &Name,
   vector<TPoint> &Points, int &Type)
{
   int ArrowLength = h_g::BitmapArrow[0]->Height;

   bool HorzFlag, VertFlag;
   int HorzType, VertType;
   TPoint HorzStartPoint, HorzEndPoint;
   TPoint VertStartPoint, VertEndPoint;
   TRect BeginRect, EndRect;
   TPoint Start, End;
   bool HideArrow = false;
   HStage *Stage;

   Stage = ControlMap[LinkMap[Name].StartControl];
   BeginRect = Stage->GetIcon()->BoundsRect;
   Stage = ControlMap[LinkMap[Name].EndControl];
   EndRect = Stage->GetIcon()->BoundsRect;

   if ( Stage->GetType() == "CONNECTOR" )
   {
      HStageConnector *Connector = (HStageConnector*)Stage;
      HStageConnector::HInfo Info;
      Connector->GetInfo(Info);
      HideArrow = Info.HideArrow;
   }

   if( BeginRect.Right < EndRect.Left )
   {
      HorzFlag = true;
      HorzStartPoint.x = BeginRect.Right;
      HorzStartPoint.y = ( BeginRect.Top + BeginRect.Bottom - 1 ) / 2;
      HorzEndPoint.x = EndRect.Left;
      HorzEndPoint.y = ( EndRect.Top + EndRect.Bottom - 1 ) / 2;
      HorzType = 0;
   }
   else if( BeginRect.Left > EndRect.Right )
   {
      HorzFlag = true;
      HorzStartPoint.x = BeginRect.Left;
      HorzStartPoint.y = ( BeginRect.Top + BeginRect.Bottom - 1 ) / 2;
      HorzEndPoint.x = EndRect.Right;
      HorzEndPoint.y = ( EndRect.Top + EndRect.Bottom - 1 ) / 2;
      HorzType = 1;
   }
   else
      HorzFlag = false;

   if( BeginRect.Bottom < EndRect.Top )
   {
      VertFlag = true;
      VertStartPoint.x = ( BeginRect.Left + BeginRect.Right - 1 ) / 2;
      VertStartPoint.y = BeginRect.Bottom;
      VertEndPoint.x = ( EndRect.Left + EndRect.Right - 1 ) / 2;
      VertEndPoint.y = EndRect.Top;
      VertType = 2;
   }
   else if( BeginRect.Top > EndRect.Bottom )
   {
      VertFlag = true;
      VertStartPoint.x = ( BeginRect.Left + BeginRect.Right - 1 ) / 2;
      VertStartPoint.y = BeginRect.Top;
      VertEndPoint.x = ( EndRect.Left + EndRect.Right - 1 ) / 2;
      VertEndPoint.y = EndRect.Bottom;
      VertType = 3;
   }
   else
      VertFlag = false;

   if( VertFlag )
   {
      Start = VertStartPoint;
      End = VertEndPoint;
      Type = VertType;
   }
   else if( HorzFlag )
   {
      Start = HorzStartPoint;
      End = HorzEndPoint;
      Type = HorzType;
   }
   else
   {
      Start.x = ( BeginRect.Left + BeginRect.Right - 2 ) / 2;
      Start.y = BeginRect.Top;
      End.x = ( EndRect.Left + EndRect.Right - 2 ) / 2;
      End.y = EndRect.Top;
      Type = 2;
   }

   int DeltaX;
   int DeltaY;
   TPoint Point;

   Points.clear();

   if ( Type == 0 || Type == 1 )
   {
      if ( Type == 0 && !HideArrow && !h_g::DirectLink )            // Right
      {
         End.x -= ArrowLength;
      }
      if ( Type == 1 && !HideArrow && !h_g::DirectLink )            // Left
      {
         End.x += ArrowLength;
      }
      DeltaX = End.x - Start.x;
      DeltaY = End.y - Start.y;
      if ( DeltaY == 0 )
      {
         Points.push_back(Start);
         Points.push_back(End);
      }
      else
      {
         Points.push_back(Start);
         Point.x = ( Start.x + End.x - 2 ) / 2;
         Point.y = Start.y;
         Points.push_back(Point);
         Point.x = ( Start.x + End.x - 2 ) / 2;
         Point.y = End.y;
         Points.push_back(Point);
         Points.push_back(End);
      }
   }
   else
   {
      if ( Type == 2 && !HideArrow && !h_g::DirectLink )            // Down
      {
         End.y -= ArrowLength;
      }
      if ( Type == 3 && !HideArrow && !h_g::DirectLink )            // Up
      {
         End.y += ArrowLength;
      }
      DeltaX = End.x - Start.x;
      DeltaY = End.y - Start.y;
      if ( DeltaX == 0 )
      {
         Points.push_back(Start);
         Points.push_back(End);
      }
      else
      {
         Points.push_back(Start);
         Point.x = Start.x;
         Point.y = ( Start.y + End.y - 2 ) / 2;
         Points.push_back(Point);
         Point.x = End.x;
         Point.y = ( Start.y + End.y - 2 ) / 2;
         Points.push_back(Point);
         Points.push_back(End);
      }
   }

   if ( HideArrow )
   {
      Type = 4;
   }
}

void __fastcall HDesignControl::DrawDragRect(const TRect &Rect)
{
   TPoint Points[5];

   if( Rect.Left == Rect.Right && Rect.Top == Rect.Bottom )
      return;

   Points[0] = TPoint(Rect.Left, Rect.Top);
   Points[1] = TPoint(Rect.Right, Rect.Top);
   Points[2] = TPoint(Rect.Right, Rect.Bottom);
   Points[3] = TPoint(Rect.Left, Rect.Bottom);
   Points[4] = TPoint(Rect.Left, Rect.Top);
   Canvas->Pen->Mode = pmXor;
   Canvas->Pen->Color = clGray;
   Canvas->Polyline(Points, 4);
   Canvas->Pen->Mode = pmCopy;
}

UTF8String __fastcall HDesignControl::FindControlByPos(int x, int y)
{
   HStageIcon *Icon;
   int MaxOrder = -1;
   UTF8String Name;

   for ( map<UTF8String, HStage*>::iterator Iter = ControlMap.begin();
         Iter != ControlMap.end(); ++Iter )
   {
      Icon = Iter->second->GetIcon();
      if( x >= Icon->Left &&
          x < Icon->Left + Icon->Width &&
          y >= Icon->Top &&
          y < Icon->Top + Icon->Height )
      {
         int Order = GetZOrder(Icon);
         if ( Order > MaxOrder )
         {
            MaxOrder = Order;
            Name = Iter->first;
         }
      }
   }

   return Name;
}

void __fastcall HDesignControl::UnselectControl(void)
{
   set<UTF8String>::iterator Iter;

   for( Iter = SelectedGroup.begin(); Iter != SelectedGroup.end(); Iter++ )
   {
      ControlMap[*Iter]->GetIcon()->NormalLight();
   }

   SelectedGroup.clear();
}

void __fastcall HDesignControl::UnselectControl(const UTF8String &Name)
{
   ControlMap[Name]->GetIcon()->NormalLight();
   SelectedGroup.erase(Name);
}

void __fastcall HDesignControl::DrawDragLine(const TRect &Rect)
{
   if( Rect.Left == Rect.Right && Rect.Top == Rect.Bottom )
      return;

   Canvas->Pen->Mode = pmXor;
   Canvas->Pen->Color = clGray;
   Canvas->MoveTo(Rect.Left, Rect.Top);
   Canvas->LineTo(Rect.Right, Rect.Bottom);
   Canvas->Pen->Mode = pmCopy;
}

void __fastcall HDesignControl::RemoveControl(const UTF8String &ControlName)
{
   vector<UTF8String> Vec;

   for( map<UTF8String, HLinkInfo>::iterator Iter = LinkMap.begin();
        Iter != LinkMap.end(); ++Iter )
   {
      if( Iter->second.StartControl == ControlName ||
          Iter->second.EndControl == ControlName )
         Vec.push_back(Iter->first);
   }

   for ( int i = 0; i < Vec.size(); i++ )
   {
      RemoveLink(Vec[i]);
   }

   if ( SelectedGroup.find(ControlName) != SelectedGroup.end() )
   {
      SelectedGroup.erase(ControlName);
   }

   RemoveIcon(ControlMap[ControlName]->GetIcon());
   delete ControlMap[ControlName];
   ControlMap.erase(ControlName);
}

UTF8String __fastcall HDesignControl::FindLinkByPos(int x, int y)
{
   TRect Rect;
   map<UTF8String, HLinkInfo>::iterator Iter;
   const int MinDelta = 5;

   for( Iter = LinkMap.begin(); Iter != LinkMap.end(); ++Iter )
   {
      if( Iter->second.StartControl == "" )
         continue;
      int Type;
      vector<TPoint> Points;
      GetLinkPoints(Iter->first, Points, Type);
      if ( h_g::DirectLink )
      {
         TPoint Begin = Points.front();
         TPoint End = Points.back();
         TRect Rect((Begin.x + End.x) / 2 - 3, (Begin.y + End.y) / 2 - 3,
            (Begin.x + End.x) / 2 + 3, (Begin.y + End.y) / 2 + 3);
         if ( x >= Rect.Left && x < Rect.Right &&
              y >= Rect.Top && y < Rect.Bottom )
         {
            return Iter->first;
         }
      }
      else
      {
         for ( int i = 0; i < Points.size() - 1; i++ )
         {
            TPoint &Start = Points[i];
            TPoint &End = Points[i + 1];
            if ( Start.x == End.x )
            {
               if ( y >= min(Start.y, End.y) && y <= max(Start.y, End.y) &&
                    abs(Start.x - x) < MinDelta )
               {
                  return Iter->first;
               }
            }
            if ( Start.y == End.y )
            {
               if ( x >= min(Start.x, End.x) && x <= max(Start.x, End.x) &&
                    abs(Start.y - y) < MinDelta )
               {
                  return Iter->first;
               }
            }
         }
      }
   }

   return "";
}

void __fastcall HDesignControl::RemoveLink(const UTF8String &Name)
{
   TRect Rect;
   HStageIcon *Control;
   vector<TRect> RectVec;

   RectVec.push_back(
      ControlMap[LinkMap[Name].StartControl]->GetIcon()->BoundsRect);
   RectVec.push_back(
      ControlMap[LinkMap[Name].EndControl]->GetIcon()->BoundsRect);
   CombineRect(RectVec, Rect);
   InvalidateRect(Handle, &Rect, true);
   LinkMap.erase(Name);
}

void __fastcall HDesignControl::RemoveLink(const UTF8String &StartJob,
   const UTF8String &EndJob)
{
   for ( map<UTF8String, HLinkInfo>::iterator Iter = LinkMap.begin();
         Iter != LinkMap.end(); Iter++ )
   {
      HLinkInfo &Link = Iter->second;
      if( Link.StartControl == StartJob &&
          Link.EndControl == EndJob )
      {
         RemoveLink(Iter->first);
         break;
      }
   }
}

UTF8String __fastcall HDesignControl::NewControlName(
   const UTF8String &Prefix)
{
   int i;
   UTF8String Name;

   for( i = 1; ; i++ )
   {
      Name = Prefix + UTF8String(i);
      if( !ControlExist(Name) )
         return Name;
   }
}

void __fastcall HDesignControl::SelectControl(const UTF8String &Name)
{
   SelectedGroup.insert(Name);
   HStageIcon *Icon = ControlMap[Name]->GetIcon();
   Icon->HighLight();
   Icon->BringToFront();
   BringToFront(Icon);
}

void __fastcall HDesignControl::CombineRect(
   const vector<TRect> &RectVec, TRect &Rect)
{
   int i;

   Rect = RectVec[0];

   for( i = 1; i < RectVec.size(); i++ )
   {
      if( RectVec[i].Left < Rect.Left )
         Rect.Left = RectVec[i].Left;
      if( RectVec[i].Right > Rect.Right )
         Rect.Right = RectVec[i].Right;
      if( RectVec[i].Top < Rect.Top )
         Rect.Top = RectVec[i].Top;
      if( RectVec[i].Bottom > Rect.Bottom )
         Rect.Bottom = RectVec[i].Bottom;
   }
}

bool __fastcall HDesignControl::ControlExist(const UTF8String &Name)
{
   return ControlMap.find(Name) != ControlMap.end();
}

void __fastcall HDesignControl::SelectGroup(const TRect &Rect)
{
   for ( map<UTF8String, HStage*>::iterator Iter = ControlMap.begin();
         Iter != ControlMap.end(); Iter++ )
   {
      int Pos1, Pos2;
      HStageIcon *Control = Iter->second->GetIcon();
      if ( Rect.Left < Rect.Right )
      {
         Pos1 = Rect.Left;
         Pos2 = Rect.Right;
      }
      else
      {
         Pos1 = Rect.Right;
         Pos2 = Rect.Left;
      }
      if ( Control->Left > Pos2 ||
           Control->Left + Control->Width <= Pos1 )
      {
         continue;
      }
      if ( Rect.Top < Rect.Bottom )
      {
         Pos1 = Rect.Top;
         Pos2 = Rect.Bottom;
      }
      else
      {
         Pos1 = Rect.Bottom;
         Pos2 = Rect.Top;
      }
      if( Control->Top > Pos2 ||
          Control->Top + Control->Height <= Pos1 )
      {
         continue;
      }
      SelectControl(Iter->first);
   }
}

bool __fastcall HDesignControl::CheckAddLink(const UTF8String &Name1,
   const UTF8String &Name2)
{
   HLinkInfo LinkInfo;
   set<UTF8String> AdjacentSet;

   for ( map<UTF8String, HLinkInfo>::iterator Iter = LinkMap.begin();
         Iter != LinkMap.end(); Iter++ )
   {
      if ( Iter->second.StartControl == Name1 &&
           Iter->second.EndControl == Name2 )
      {
         goto error;
      }
      if ( Iter->second.StartControl == Name2 &&
           Iter->second.EndControl == Name1 )
      {
         goto error;
      }
   }

   GetAdjacentSet(Name2, AdjacentSet);

   if ( AdjacentSet.find(Name1) != AdjacentSet.end() )
   {
      goto error;
   }

   if ( ControlMap[Name2]->GetType() == "EXTERN_JOB" )
   {
      goto error;
   }

   return true;

error :

   {
      char TmpStr[200 + 1];
      snprintf(TmpStr, 200, h_g::I18nString("h_designcontrol:CAN_NOT_LINK"),
         Name1.c_str(), Name2.c_str());
      ShowMessage(UTF8String(TmpStr));
      return false;
   }
}

void __fastcall HDesignControl::AddLink(const UTF8String &Name1,
   const UTF8String &Name2)
{
   if ( ControlMap.find(Name1) == ControlMap.end() ||
        ControlMap.find(Name2) == ControlMap.end() )
   {
      return;
   }
   
   TRect Rect;
   HStageIcon *Control;
   vector<TRect> RectVec;
   HLinkInfo LinkInfo;

   LinkInfo.Name = NewLinkName();
   LinkInfo.StartControl = Name1;
   LinkInfo.EndControl = Name2;
   LinkMap[LinkInfo.Name] = LinkInfo;
   AdjustLinkLabel(LinkInfo.Name);

   RectVec.push_back(ControlMap[Name1]->GetIcon()->BoundsRect);
   RectVec.push_back(ControlMap[Name2]->GetIcon()->BoundsRect);
   CombineRect(RectVec, Rect);
   InvalidateRect(Handle, &Rect, true);
}

void __fastcall HDesignControl::GetAdjacentSet(const UTF8String &StartControl,
   set<UTF8String> &Result)
{
   for( map<UTF8String, HLinkInfo>::iterator Iter = LinkMap.begin();
        Iter != LinkMap.end(); Iter++ )
   {
      if( Iter->second.StartControl != StartControl )
         continue;
      if( Result.find(Iter->second.EndControl) == Result.end() )
      {
         Result.insert(Iter->second.EndControl);
         GetAdjacentSet(Iter->second.EndControl, Result);
      }
   }
}

UTF8String __fastcall HDesignControl::NewLinkName(void)
{
   int i;
   UTF8String Name;
   UTF8String Prefix = "LINK_";

   for( i = 1; ; i++ )
   {
      Name = Prefix + UTF8String(i);
      if( !LinkExist(Name) )
         return Name;
   }
}

bool __fastcall HDesignControl::LinkExist(const UTF8String &Name)
{
   return LinkMap.find(Name) != LinkMap.end();
}

void __fastcall HDesignControl::OnDeactivate(void)
{
   CancelDragAction();
}

void __fastcall HDesignControl::SaveLinkToXmlTree(const UTF8String &Name,
   HXmlTree *XmlTree)
{
   HLinkInfo &Link = LinkMap[Name];
   HXmlTree *Node = new HXmlTree(XmlTree, "LINK");
   Node->SetProperty("START_JOB", Link.StartControl.c_str());
   Node->SetProperty("END_JOB", Link.EndControl.c_str());
}

void __fastcall HDesignControl::SaveJobToXmlTree(const UTF8String &Name,
   HXmlTree *XmlTree)
{
   UTF8String TmpStr;
   HXmlTree *Node = new HXmlTree(XmlTree, "JOB");
   HStage *Stage = ControlMap[Name];
   Node->SetProperty("NAME", Name.c_str());
   Node->SetProperty("TYPE", Stage->GetType().c_str());
   TmpStr.sprintf("%d", Stage->GetIcon()->Left +
      HorzScrollBar->Position);
   Node->SetProperty("X", TmpStr.c_str());
   TmpStr.sprintf("%d", Stage->GetIcon()->Top +
      VertScrollBar->Position);
   Node->SetProperty("Y", TmpStr.c_str());
   Stage->SaveProperty(Node);
}

void __fastcall HDesignControl::SaveToXmlTree(HXmlTree *XmlTree)
{
   map<UTF8String, HLinkInfo> TmpLinkMap;
   
   for ( map<UTF8String, HLinkInfo>::iterator Iter = LinkMap.begin();
         Iter != LinkMap.end(); ++Iter )
   {
      HLinkInfo &Link = Iter->second;
      UTF8String Key =  Link.StartControl + ":::" + Link.EndControl;
      TmpLinkMap[Key] = Link;
   }

   for ( map<UTF8String, HLinkInfo>::iterator Iter = TmpLinkMap.begin();
         Iter != TmpLinkMap.end(); ++Iter )
   {
      HLinkInfo &Link = Iter->second;
      SaveLinkToXmlTree(Link.Name, XmlTree);
   }

   for ( map<UTF8String, HStage*>::iterator Iter = ControlMap.begin();
         Iter != ControlMap.end(); ++Iter )
   {
      SaveJobToXmlTree(Iter->first, XmlTree);
   }
}

void __fastcall HDesignControl::LoadJobFromXmlTree(HXmlTree *XmlTree, bool XmlPropertyFlag)
{
   UTF8String Name;
   UTF8String Type;

   if ( XmlPropertyFlag )
   {
      Name = XmlTree->GetPropertyUpper("NAME").c_str();
      Type = XmlTree->GetPropertyUpper("TYPE").c_str();
   }
   else
   {
      Type = XmlTree->GetValueUpper("TYPE").c_str();
      Name = XmlTree->GetValueUpper("NAME").c_str();
   }

   HStage *Stage = h_g::CloneStage(Type);
   ControlMap[Name] = Stage;
   Stage->SetName(Name);
   Stage->LoadProperty(XmlTree, XmlPropertyFlag);
   Stage->CreateIcon(this);
   int x;
   int y;
   if ( XmlPropertyFlag )
   {
      x = UTF8String(XmlTree->GetProperty("X").c_str()).ToIntDef(0);
      y = UTF8String(XmlTree->GetProperty("Y").c_str()).ToIntDef(0);
   }
   else
   {
      x = UTF8String(XmlTree->GetValue("X").c_str()).ToIntDef(0);
      y = UTF8String(XmlTree->GetValue("Y").c_str()).ToIntDef(0);
   }
   Stage->Move(x, y);
   AddIcon(Stage->GetIcon());
}

void __fastcall HDesignControl::LoadFromXmlTree(HXmlTree *XmlTree, bool XmlPropertyFlag)
{
   vector<HXmlTree*> Nodes;

   XmlTree->GetChildren("JOB", Nodes);

   for( int i = 0; i < Nodes.size(); i++ )
   {
      LoadJobFromXmlTree(Nodes[i], XmlPropertyFlag);
   }

   XmlTree->GetChildren("LINK", Nodes);

   for( int i = 0; i < Nodes.size(); i++ )
   {
      UTF8String StartJob;
      UTF8String EndJob;
      if ( XmlPropertyFlag )
      {
         StartJob = Nodes[i]->GetPropertyUpper("START_JOB").c_str();
         EndJob = Nodes[i]->GetPropertyUpper("END_JOB").c_str();
      }
      else
      {
         StartJob = Nodes[i]->GetValueUpper("START_JOB").c_str();
         EndJob = Nodes[i]->GetValueUpper("END_JOB").c_str();
      }
      AddLink(StartJob, EndJob);
   }
}

UTF8String __fastcall HDesignControl::GetGotoThisGroup(void)
{
   if ( SelectedGroup.size() != 1 )
   {
      return "";
   }

   HStageGroup *Group =
      dynamic_cast<HStageGroup*>(ControlMap[*SelectedGroup.begin()]);

   if ( Group != NULL )
   {
      HStageGroup::HInfo Info;
      Group->GetInfo(Info);
      return Info.Group;
   }

   HStageExternJob *ExternJob =
      dynamic_cast<HStageExternJob*>(ControlMap[*SelectedGroup.begin()]);

   if ( ExternJob != NULL )
   {
      HStageExternJob::HInfo Info;
      ExternJob->GetInfo(Info);
      if ( Info.ExternGroup == "" )
      {
         return "";
      }
      return Info.ExternGroup + h_g::JobNameDelimiter + Info.ExternJob;
   }

   return "";
}

void __fastcall HDesignControl::DrawDragTrack()
{
   if ( DragAction == daNone )
   {
      return;
   }

   if ( DragAction == daBox )
   {
      TRect Rect;
      Rect.Left = DragX;
      Rect.Top = DragY;
      Rect.Right = DragX + DragDeltaX;
      Rect.Bottom = DragY + DragDeltaY;
      DrawDragRect(Rect);
      return;
   }

   if ( DragAction == daDrag || DragAction == daMove )
   {
      for( set<UTF8String>::iterator Iter = SelectedGroup.begin();
           Iter != SelectedGroup.end(); Iter++ )
      {
         HStageIcon *Control = ControlMap[*Iter]->GetIcon();
         TRect Rect;
         Rect.Left = Control->Left + DragDeltaX;
         Rect.Right = Control->Left + Control->Width + DragDeltaX;
         Rect.Top = Control->Top + DragDeltaY;
         Rect.Bottom = Control->Top + Control->Height + DragDeltaY;
         DrawDragRect(Rect);
      }
      return;
   }

   if ( DragAction == daLink )
   {
      TRect Rect;
      Rect.Left = DragX;
      Rect.Top = DragY;
      Rect.Right = DragX + DragDeltaX;
      Rect.Bottom = DragY + DragDeltaY;
      DrawDragLine(Rect);
      return;
   }
}

void __fastcall HDesignControl::CancelDragAction()
{
   if ( DragAction == daBox || DragAction == daDrag || DragAction == daLink )
   {
      ClipCursor(NULL);
   }

   DrawDragTrack();
   DragAction = daNone;
}

void __fastcall HDesignControl::EndDragAction()
{
   if ( DragAction == daBox || DragAction == daDrag || DragAction == daLink )
   {
      ClipCursor(NULL);
   }

   DrawDragTrack();

   if ( DragAction == daBox )
   {
      TRect Rect;
      Rect.Left = DragX;
      Rect.Top = DragY;
      Rect.Right = DragX + DragDeltaX;
      Rect.Bottom = DragY + DragDeltaY;
      SelectGroup(Rect);
   }

   if ( DragAction == daDrag || DragAction == daMove )
   {
      if ( DragAction == daDrag && DragDeltaX == 0 && DragDeltaY == 0 )
      {
         UnselectControl();
         SelectControl(FindControlByPos(DragX, DragY));
      }
      else
      {
         HCmdMove *Cmd = new HCmdMove(SelectedGroup, DragDeltaX, DragDeltaY);
         MainForm->PushUndoStack(Cmd);
      }
   }

   if ( DragAction == daLink )
   {
      UTF8String StartName = FindControlByPos(DragX, DragY);
      UTF8String EndName = FindControlByPos(
         DragX + DragDeltaX, DragY + DragDeltaY);
      if( StartName != "" && EndName != "" && StartName != EndName )
      {
         if ( CheckAddLink(StartName, EndName) )
         {
            HCmdAddLink *Cmd = new HCmdAddLink(StartName, EndName);
            MainForm->PushUndoStack(Cmd);
         }
      }
   }

   DragAction = daNone;
}

void __fastcall HDesignControl::AddBoundsRect(vector<TRect> &RectVec,
   const set<UTF8String> &JobSet)
{
   for ( set<UTF8String>::const_iterator Iter = JobSet.begin();
         Iter != JobSet.end(); Iter++ )
   {
      HStageIcon *Icon;
      Icon = ControlMap[*Iter]->GetIcon();
      RectVec.push_back(Icon->BoundsRect);
      for ( map<UTF8String, HLinkInfo>::iterator
            LinkIter = LinkMap.begin();
            LinkIter != LinkMap.end(); LinkIter++ )
      {
         HLinkInfo &Link = LinkIter->second;
         if ( Link.StartControl == *Iter )
         {
            Icon = ControlMap[Link.EndControl]->GetIcon();
         }
         else if( Link.EndControl == *Iter )
         {
            Icon = ControlMap[Link.StartControl]->GetIcon();
         }
         else
         {
            continue;
         }
         RectVec.push_back(Icon->BoundsRect);
      }
   }
}

void __fastcall HDesignControl::AdjustLinkLabel(const UTF8String &Name)
{
   int Type;

   GetLinkPoints(Name, LinkMap[Name].LinePoints, Type);
}

void __fastcall HDesignControl::AddIcon(HStageIcon *Icon)
{
   IconList.push_back(Icon);
}

void __fastcall HDesignControl::RemoveIcon(HStageIcon *Icon)
{
   IconList.remove(Icon);
}

void __fastcall HDesignControl::BringToFront(HStageIcon *Icon)
{
   IconList.remove(Icon);
   IconList.push_back(Icon);
}

int __fastcall HDesignControl::GetZOrder(HStageIcon *Icon)
{
   int Order = 0;

   for ( list<HStageIcon*>::iterator Iter = IconList.begin();
         Iter != IconList.end(); Iter++, Order++ )
   {
      if ( *Iter == Icon )
      {
         break;
      }
   }

   return Order;
}

void __fastcall HDesignControl::SelectAll()
{
   for ( map<UTF8String, HStage*>::iterator Iter = ControlMap.begin();
         Iter != ControlMap.end(); Iter++ )
   {
      SelectControl(Iter->first);
   }
}

void __fastcall HDesignControl::SelectHome()
{
   if ( SelectedGroup.empty() )
   {
      return;
   }

   HStage *SelectedStage = ControlMap[*SelectedGroup.begin()];
   int SelectedY = SelectedStage->GetIcon()->Top;

   for ( map<UTF8String, HStage*>::iterator Iter = ControlMap.begin();
         Iter != ControlMap.end(); Iter++ )
   {
      HStage *Stage = Iter->second;
      int y = Stage->GetIcon()->Top;
      if ( y <= SelectedY )
      {
         SelectControl(Iter->first);
      }
   }
}

void __fastcall HDesignControl::SelectEnd()
{
   if ( SelectedGroup.empty() )
   {
      return;
   }

   HStage *SelectedStage = ControlMap[*SelectedGroup.begin()];
   int SelectedY = SelectedStage->GetIcon()->Top;

   for ( map<UTF8String, HStage*>::iterator Iter = ControlMap.begin();
         Iter != ControlMap.end(); Iter++ )
   {
      HStage *Stage = Iter->second;
      int y = Stage->GetIcon()->Top;
      if ( y >= SelectedY )
      {
         SelectControl(Iter->first);
      }
   }
}

bool __fastcall HDesignControl::CanDelete()
{
   return !SelectedGroup.empty();
}

void __fastcall HDesignControl::DoDelete()
{
   CancelDragAction();

   if ( SelectedGroup.empty() )
   {
      return;
   }

   HCmdDeleteJob *Cmd = new HCmdDeleteJob(SelectedGroup);
   MainForm->PushUndoStack(Cmd);
}

bool __fastcall HDesignControl::CanEditProperty()
{
   return SelectedGroup.size() == 1;
}

void __fastcall HDesignControl::DoEditProperty()
{
   UTF8String OldName = *SelectedGroup.begin();
   HStage *Stage = ControlMap[OldName];
   HXmlTree *OldXml = new HXmlTree;
   Stage->SaveProperty(OldXml);

   if ( !Stage->EditProperty() )
   {
      delete OldXml;
      return;
   }

   UTF8String NewName = Stage->GetName();
   HXmlTree *NewXml = new HXmlTree;
   Stage->SaveProperty(NewXml);

   if ( OldName == NewName && *OldXml == *NewXml )
   {
      delete OldXml;
      delete NewXml;
      return;
   }

   HCmdEditProperty *Cmd = new HCmdEditProperty(
      OldName, OldXml, NewName, NewXml);
   MainForm->PushUndoStack(Cmd);
}

bool __fastcall HDesignControl::CanPaste()
{
   return Clipboard()->HasFormat(h_g::ClipboardFormat);
}

void __fastcall HDesignControl::DoCut()
{
   DoCopy();
   DoDelete();
}

void __fastcall HDesignControl::DoCopy()
{
   HXmlTree Xml;
   HXmlTree *XmlTree = &Xml;

   for ( map<UTF8String, HLinkInfo>::iterator Iter = LinkMap.begin();
         Iter != LinkMap.end(); Iter++ )
   {
      HLinkInfo &Link = Iter->second;
      if ( SelectedGroup.find(Link.StartControl) == SelectedGroup.end() ||
           SelectedGroup.find(Link.EndControl) == SelectedGroup.end() )
      {
         continue;
      }
      SaveLinkToXmlTree(Iter->first, XmlTree);
   }

   for ( set<UTF8String>::iterator Iter = SelectedGroup.begin();
         Iter != SelectedGroup.end(); Iter++ )
   {
      SaveJobToXmlTree(*Iter, XmlTree);
   }

   h_base::HMemStream Stream;
   XmlTree->SaveToStream(&Stream);
   Clipboard()->Clear();
   Clipboard()->Open();

   {
      HGLOBAL Data = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE,
         Stream.GetLength());
      void *p = GlobalLock(Data);
      memcpy(p, Stream.GetBuf(), Stream.GetLength());
      SetClipboardData(h_g::ClipboardFormat, Data);
      GlobalUnlock(Data);
   }

   Clipboard()->Close();
}

void __fastcall HDesignControl::DoPaste()
{
   UnselectControl();
   Clipboard()->Open();
   HGLOBAL Data = GetClipboardData(h_g::ClipboardFormat);
   void *p = GlobalLock(Data);
   h_base::HMemStream Stream(p, GlobalSize(Data));
   GlobalUnlock(Data);
   HXmlTree *XmlTree = new HXmlTree;
   XmlTree->LoadFromStream(&Stream);
   Clipboard()->Close();

   int Delta = 0;

   if ( !ControlMap.empty() )
   {
      Delta = 8;
   }

   HCmdPaste *Cmd = new HCmdPaste(XmlTree, Delta);
   MainForm->PushUndoStack(Cmd);
}

void __fastcall HDesignControl::RefreshCaption()
{
   for( map<UTF8String, HStage*>::iterator Iter = ControlMap.begin();
        Iter != ControlMap.end(); ++Iter )
      Iter->second->RefreshCaption();
}

void __fastcall HCommand::SavePageStat()
{
   FPageIndex = MainForm->GetPageIndex();
   FHScrollPos = MainForm->CurrentDesignControl()->HorzScrollBar->Position;
   FVScrollPos = MainForm->CurrentDesignControl()->VertScrollBar->Position;
}

void __fastcall HCommand::RestorePageStat()
{
   MainForm->SetPageIndex(FPageIndex);
   MainForm->CurrentDesignControl()->HorzScrollBar->Position = FHScrollPos;
   MainForm->CurrentDesignControl()->VertScrollBar->Position = FVScrollPos;
}

void __fastcall HCmdNewJob::DoExecute()
{
   HDesignControl *Control = MainForm->CurrentDesignControl();
   HStage *Stage = h_g::CloneStage(FType);
   FName = Control->NewControlName(Stage->GetType() + "_");
   Control->ControlMap[FName] = Stage;
   Stage->SetName(FName);
   Stage->CreateIcon(Control);
   Stage->Move(Fx, Fy);
   Control->AddIcon(Stage->GetIcon());
   Control->UnselectControl();
   Control->SelectControl(FName);

   char TmpStr[200 + 1];
   snprintf(TmpStr, 200, h_g::I18nString("h_designcontrol:ADD"),
      FName.c_str());
   FCaption = TmpStr;
}

void __fastcall HCmdNewJob::DoUnexecute()
{
   RestorePageStat();
   MainForm->CurrentDesignControl()->RemoveControl(FName);
}

void __fastcall HCmdDeleteJob::DoExecute()
{
   HDesignControl *Control = MainForm->CurrentDesignControl();
   FXml->Clear();

   for ( map<UTF8String, HDesignControl::HLinkInfo>::iterator
         Iter = Control->LinkMap.begin();
         Iter != Control->LinkMap.end(); Iter++ )
   {
      HDesignControl::HLinkInfo &Link = Iter->second;
      if ( FJobSet.find(Link.StartControl) == FJobSet.end() &&
           FJobSet.find(Link.EndControl) == FJobSet.end() )
      {
         continue;
      }
      Control->SaveLinkToXmlTree(Iter->first, FXml);
   }

   for ( set<UTF8String>::iterator Iter = FJobSet.begin();
         Iter != FJobSet.end(); Iter++ )
   {
      Control->SaveJobToXmlTree(*Iter, FXml);
   }

   vector<TRect> RectVec;
   Control->AddBoundsRect(RectVec, FJobSet);
   TRect Rect;
   Control->CombineRect(RectVec, Rect);
   InvalidateRect(Control->Handle, &Rect, true);

   for ( set<UTF8String>::iterator Iter = FJobSet.begin();
         Iter != FJobSet.end(); Iter++ )
   {
      Control->RemoveControl(*Iter);
   }

   char TmpStr[200 + 1];
   snprintf(TmpStr, 200, h_g::I18nString("h_designcontrol:DELETE"),
      (*FJobSet.begin()).c_str());
   FCaption = TmpStr;

   if ( FJobSet.size() > 1 )
   {
      FCaption += "...";
   }
}

void __fastcall HCmdDeleteJob::DoUnexecute()
{
   RestorePageStat();
   HDesignControl *Control = MainForm->CurrentDesignControl();
   Control->LoadFromXmlTree(FXml, true);
}

void __fastcall HCmdAddLink::DoExecute()
{
   HDesignControl *Control = MainForm->CurrentDesignControl();
   Control->AddLink(FStartJob, FEndJob);
   char TmpStr[200 + 1];
   snprintf(TmpStr, 200, h_g::I18nString("h_designcontrol:LINK"),
      FStartJob.c_str(), FEndJob.c_str());
   FCaption = TmpStr;
}

void __fastcall HCmdAddLink::DoUnexecute()
{
   RestorePageStat();
   HDesignControl *Control = MainForm->CurrentDesignControl();
   Control->RemoveLink(FStartJob, FEndJob);
}

void __fastcall HCmdRemoveLink::DoExecute()
{
   HDesignControl *Control = MainForm->CurrentDesignControl();
   Control->RemoveLink(FStartJob, FEndJob);
   char TmpStr[200 + 1];
   snprintf(TmpStr, 200, h_g::I18nString("h_designcontrol:UNLINK"),
      FStartJob.c_str(), FEndJob.c_str());
   FCaption = TmpStr;
}

void __fastcall HCmdRemoveLink::DoUnexecute()
{
   RestorePageStat();
   HDesignControl *Control = MainForm->CurrentDesignControl();
   Control->AddLink(FStartJob, FEndJob);
}

void __fastcall HCmdMove::DoExecute()
{
   HDesignControl *Control = MainForm->CurrentDesignControl();
   vector<TRect> RectVec;
   Control->AddBoundsRect(RectVec, FJobSet);

   for ( set<UTF8String>::iterator Iter = FJobSet.begin();
         Iter != FJobSet.end(); Iter++ )
   {
      HStage *Stage = Control->ControlMap[*Iter];
      HStageIcon *Icon;
      Icon = Stage->GetIcon();
      Stage->Move(Icon->BoundsRect.Left + FDeltaX,
         Icon->BoundsRect.Top + FDeltaY);
      for ( map<UTF8String, HDesignControl::HLinkInfo>::iterator
            LinkIter = Control->LinkMap.begin();
            LinkIter != Control->LinkMap.end(); LinkIter++ )
      {
         HDesignControl::HLinkInfo &Link = LinkIter->second;
         if ( Link.StartControl == *Iter || Link.EndControl == *Iter )
         {
            Control->AdjustLinkLabel(LinkIter->first);
         }
      }
   }

   Control->AddBoundsRect(RectVec, FJobSet);
   TRect Rect;
   Control->CombineRect(RectVec, Rect);
   InvalidateRect(Control->Handle, &Rect, true);
   char TmpStr[200 + 1];
   snprintf(TmpStr, 200, h_g::I18nString("h_designcontrol:MOVE"),
      (*FJobSet.begin()).c_str());
   FCaption = TmpStr;

   if ( FJobSet.size() > 1 )
   {
      FCaption += "...";
   }
}

void __fastcall HCmdMove::DoUnexecute()
{
   RestorePageStat();
   HDesignControl *Control = MainForm->CurrentDesignControl();
   vector<TRect> RectVec;
   Control->AddBoundsRect(RectVec, FJobSet);

   for ( set<UTF8String>::iterator Iter = FJobSet.begin();
         Iter != FJobSet.end(); Iter++ )
   {
      HStage *Stage = Control->ControlMap[*Iter];
      HStageIcon *Icon;
      Icon = Stage->GetIcon();
      Stage->Move(Icon->BoundsRect.Left - FDeltaX,
         Icon->BoundsRect.Top - FDeltaY);
      for ( map<UTF8String, HDesignControl::HLinkInfo>::iterator
            LinkIter = Control->LinkMap.begin();
            LinkIter != Control->LinkMap.end(); LinkIter++ )
      {
         HDesignControl::HLinkInfo &Link = LinkIter->second;
         if ( Link.StartControl == *Iter || Link.EndControl == *Iter )
         {
            Control->AdjustLinkLabel(LinkIter->first);
         }
      }
   }

   Control->AddBoundsRect(RectVec, FJobSet);
   TRect Rect;
   Control->CombineRect(RectVec, Rect);
   InvalidateRect(Control->Handle, &Rect, true);
}

void __fastcall HCmdPaste::DoExecute()
{
   HDesignControl *Control = MainForm->CurrentDesignControl();
   vector<HXmlTree*> Nodes;
   vector<HDesignControl::HLinkInfo> Links;
   FXml->GetChildren("LINK", Nodes);

   for( int i = 0; i < Nodes.size(); i++ )
   {
      HDesignControl::HLinkInfo Link;
      Link.StartControl = Nodes[i]->GetPropertyUpper("START_JOB").c_str();
      Link.EndControl = Nodes[i]->GetPropertyUpper("END_JOB").c_str();
      Links.push_back(Link);
   }

   FXml->GetChildren("JOB", Nodes);

   for( int i = 0; i < Nodes.size(); i++ )
   {
      HXmlTree *Job = Nodes[i];
      UTF8String Name = Job->GetPropertyUpper("NAME").c_str();
      if ( Control->ControlExist(Name) )
      {
         UTF8String NewName = Control->NewControlName(
            (Job->GetPropertyUpper("TYPE") + "_").c_str());
         for ( int j = 0; j < Links.size(); j++ )
         {
            HDesignControl::HLinkInfo &Link = Links[j];
            if ( Link.StartControl == Name )
            {
               Link.StartControl = NewName;
            }
            if ( Link.EndControl == Name )
            {
               Link.EndControl = NewName;
            }
         }
         Name = NewName;
         Job->SetProperty("NAME", Name.c_str());
      }
      int x = UTF8String(Job->GetProperty("X").c_str()).ToIntDef(0) -
         Control->HorzScrollBar->Position + FDelta;
      int y = UTF8String(Job->GetProperty("Y").c_str()).ToIntDef(0) -
         Control->VertScrollBar->Position + FDelta;
      Job->SetProperty("X", UTF8String(x).c_str());
      Job->SetProperty("Y", UTF8String(y).c_str());
      Control->LoadJobFromXmlTree(Nodes[i], true);
      Control->SelectControl(Name);
   }

   for ( int j = 0; j < Links.size(); j++ )
   {
      HDesignControl::HLinkInfo &Link = Links[j];
      Control->AddLink(Link.StartControl, Link.EndControl);
   }

   FDelta = 0;
   FCaption = h_g::I18nString("h_designcontrol:PASTE");
}

void __fastcall HCmdPaste::DoUnexecute()
{
   RestorePageStat();
   HDesignControl *Control = MainForm->CurrentDesignControl();
   vector<HXmlTree*> Nodes;
   FXml->GetChildren("JOB", Nodes);

   for( int i = 0; i < Nodes.size(); i++ )
   {
      HXmlTree *Job = Nodes[i];
      UTF8String Name = Job->GetPropertyUpper("NAME").c_str();
      Control->RemoveControl(Name);
   }
}

void __fastcall HCmdAddGroup::DoExecute()
{
   FPageIndex = MainForm->NewGroup();
   FCaption = h_g::I18nString("h_designcontrol:NEW_GROUP");
}

void __fastcall HCmdAddGroup::DoUnexecute()
{
   MainForm->RemovePage(FPageIndex);
   RestorePageStat();
}

void __fastcall HCmdRemoveGroup::DoExecute()
{
   FGroupName = MainForm->GetGroupName(FPageIndex);
   MainForm->RemovePage(FPageIndex);
   FCaption = h_g::I18nString("h_designcontrol:DELETE_GROUP");
}

void __fastcall HCmdRemoveGroup::DoUnexecute()
{
   int NewPageIndex = MainForm->NewGroup(FGroupName);
   MainForm->MoveGroup(NewPageIndex, FPageIndex);
   MainForm->CurrentDesignControl()->LoadFromXmlTree(FXml, true);
   RestorePageStat();
}

void __fastcall HCmdMoveGroup::DoExecute()
{
   MainForm->MoveGroup(FIndexFrom, FIndexTo);
   FCaption = h_g::I18nString("h_designcontrol:MOVE_GROUP");
}

void __fastcall HCmdMoveGroup::DoUnexecute()
{
   MainForm->MoveGroup(FIndexTo, FIndexFrom);
   RestorePageStat();
}

void __fastcall HCmdLoadGroup::DoExecute()
{
   vector<HXmlTree*> Nodes;
   FXml->GetChildren("GROUP", Nodes);

   for ( int i = 0; i < Nodes.size(); i++ )
   {
      UTF8String Name;
      if ( FXmlPropertyFlag )
      {
         Name = Nodes[i]->GetPropertyUpper("NAME").c_str();
      }
      else
      {
         Name = Nodes[i]->GetValueUpper("NAME").c_str();
      }
      MainForm->NewGroup(Name);
      MainForm->Groups.back().DesignControl->LoadFromXmlTree(Nodes[i], FXmlPropertyFlag);
   }

   FCaption = h_g::I18nString("h_designcontrol:LOAD_GROUP");
}

void __fastcall HCmdLoadGroup::DoUnexecute()
{
   vector<HXmlTree*> Nodes;
   FXml->GetChildren("GROUP", Nodes);

   for ( int i = 0; i < Nodes.size(); i++ )
   {
      MainForm->RemovePage(MainForm->Groups.size() - 1);
   }

   RestorePageStat();
}

void __fastcall HCmdEditProperty::DoExecute()
{
   HDesignControl *Control = MainForm->CurrentDesignControl();
   HStage *Stage = Control->ControlMap[FOldName];

   Stage->SetName(FNewName);
   Stage->LoadProperty(FNewXml, true);
   Stage->RefreshCaption();

   if( FNewName != FOldName )
   {
      Control->ControlMap.erase(FOldName);
      Control->ControlMap[FNewName] = Stage;
      for ( map<UTF8String, HDesignControl::HLinkInfo>::iterator
            Iter = Control->LinkMap.begin();
            Iter != Control->LinkMap.end(); Iter++ )
      {
         if( Iter->second.StartControl == FOldName )
         {
            Iter->second.StartControl = FNewName;
         }
         if( Iter->second.EndControl == FOldName )
         {
            Iter->second.EndControl = FNewName;
         }
      }
      Control->SelectedGroup.erase(FOldName);
      Control->SelectedGroup.insert(FNewName);
   }

   if ( Stage->GetType() == "CONNECTOR" )
   {
      set<UTF8String> JobSet;
      JobSet.insert(FNewName);
      vector<TRect> RectVec;
      Control->AddBoundsRect(RectVec, JobSet);
      TRect Rect;
      Control->CombineRect(RectVec, Rect);
      InvalidateRect(Control->Handle, &Rect, true);
   }

   char TmpStr[200 + 1];
   snprintf(TmpStr, 200, h_g::I18nString("h_designcontrol:EDIT"),
      FOldName.c_str());
   FCaption = TmpStr;
}

void __fastcall HCmdEditProperty::DoUnexecute()
{
   RestorePageStat();
   HDesignControl *Control = MainForm->CurrentDesignControl();
   HStage *Stage = Control->ControlMap[FNewName];

   Stage->SetName(FOldName);
   Stage->LoadProperty(FOldXml, true);
   Stage->RefreshCaption();

   if( FNewName != FOldName )
   {
      Control->ControlMap.erase(FNewName);
      Control->ControlMap[FOldName] = Stage;
      for ( map<UTF8String, HDesignControl::HLinkInfo>::iterator
            Iter = Control->LinkMap.begin();
            Iter != Control->LinkMap.end(); Iter++ )
      {
         if( Iter->second.StartControl == FNewName )
         {
            Iter->second.StartControl = FOldName;
         }
         if( Iter->second.EndControl == FNewName )
         {
            Iter->second.EndControl = FOldName;
         }
      }
      Control->SelectedGroup.erase(FNewName);
      Control->SelectedGroup.insert(FOldName);
   }

   if ( Stage->GetType() == "CONNECTOR" )
   {
      set<UTF8String> JobSet;
      JobSet.insert(FOldName);
      vector<TRect> RectVec;
      Control->AddBoundsRect(RectVec, JobSet);
      TRect Rect;
      Control->CombineRect(RectVec, Rect);
      InvalidateRect(Control->Handle, &Rect, true);
   }
}

void __fastcall HCmdEditBatchProperty::DoExecute()
{
   MainForm->LoadInfoFromXmlTree(FNewXml, true);

   if( FNewName != FOldName )
   {
      MainForm->PageControl1->ActivePage->Caption = FNewName;
   }

   FCaption = h_g::I18nString("h_designcontrol:EDIT_BATCH_PROPERTY");
}

void __fastcall HCmdEditBatchProperty::DoUnexecute()
{
   MainForm->LoadInfoFromXmlTree(FOldXml, true);

   if( FNewName != FOldName )
   {
      MainForm->PageControl1->ActivePage->Caption = FOldName;
   }
}

void __fastcall HDesignControl::SaveToJobList(map<UTF8String, HJobListInfo> &JobMap)
{
   TTabSheet *TabSheet = (TTabSheet*)Parent;
   UTF8String GroupId = TabSheet->Caption;
   
   for( map<UTF8String, HStage*>::iterator Iter = ControlMap.begin();
        Iter != ControlMap.end(); ++Iter )
   {
      HStage *Stage = Iter->second;
      UTF8String Type = Stage->GetType();
      if ( Type != "JOB" && Type != "EVENT_FIRE" && Type != "EVENT_WAIT" &&
           Type != "GROUP" && Type != "TIME" && Type != "BATCH" )
      {
         continue;
      }
      HJobListInfo Info;
      Stage->GetJobListInfo(GroupId, Info);
      JobMap[Info.Name] = Info;
   }
}

HStage * __fastcall HDesignControl::FindControlByName(const UTF8String &Name)
{
   if ( ControlMap.find(Name) == ControlMap.end() )
   {
      return NULL;
   }

   return ControlMap[Name];
}

void __fastcall HDesignControl::SetFocusedJob(const UTF8String &Name,
   bool ScrollToCenter)
{
   UTF8String NewName = Name;

   if ( ControlMap.find(NewName) == ControlMap.end() )
   {
      NewName = "";
   }
   else if ( ScrollToCenter )
   {
      HStageIcon *Icon = ControlMap[NewName]->GetIcon();
      int DeltaX = ClientWidth / 2 - ( Icon->Left + Icon->Width / 2 );
      int DeltaY = ClientHeight / 2 - ( Icon->Top + Icon->Height / 2 );
      int PosX = HorzScrollBar->Position;
      int PosY = VertScrollBar->Position;
      PosX -= DeltaX;
      PosY -= DeltaY;
      HorzScrollBar->Position = PosX;
      VertScrollBar->Position = PosY;
   }

   if ( ( SelectedGroup.empty() && NewName == "" ) ||
        ( SelectedGroup.size() == 1 && *SelectedGroup.begin() == NewName ) )
   {
      return;
   }

   UnselectControl();

   if ( NewName != "" )
   {
      SelectControl(NewName);
   }

   Repaint();
}

#pragma package(smart_init)
